package nila;

enum T_Lex {
    SYM, STRING, NUM,
    PIPE, NL, SEMI, COL, SHARP,
    RBRACE, LBRACE,
    LSQUARE, RSQUARE,
    LPAREN, RPAREN,
    LSLASH, RSLASH,
    QUOTE, DQUOTE, TICK,
    EOF
};

interface I_Lex {
    T_Lex next();
    String read_str(String c);
    Number read_num();
    int read_sym();
    boolean eof();
}

public class Lex implements I_Lex {
    I_Reader _reader = null;
    public Lex(I_Reader r) {
        _reader = r;
    }
    public static boolean debug = false;
    void d(String v) {
        if (debug) I.d("lex",v);
    }
    T_Lex adv(T_Lex t) {
        _reader.next();
        return t;
    }
    public T_Lex next() {
        if (!consume_space()) return T_Lex.EOF;
        char ch = _reader.peek();
        d(ch + "");
        switch (ch) {
            case '\0': return adv(T_Lex.EOF);
            case '|': return adv(T_Lex.PIPE);
            case '{': return adv(T_Lex.LBRACE);
            case '}': return adv(T_Lex.RBRACE);
            case '<': return adv(T_Lex.LSQUARE);
            case '>': return adv(T_Lex.RSQUARE);
            case '(': return adv(T_Lex.LPAREN);
            case ')': return adv(T_Lex.RPAREN);
            case '\\': return adv(T_Lex.LSLASH);
            case '/': return adv(T_Lex.RSLASH);
            case '"': return adv(T_Lex.DQUOTE);
            case '`': return adv(T_Lex.TICK);
            case '\'': return adv(T_Lex.QUOTE);
            case '\n': return adv(T_Lex.NL);
            case ';': return adv(T_Lex.SEMI);
            case ':': return adv(T_Lex.COL);
            case '#': return adv(T_Lex.SHARP);
            default: return Character.isDigit(ch) ? T_Lex.NUM : T_Lex.SYM ;
        }
    }

    String read_till(String c) {
        char cx = '\0';
        StringBuffer sb = new StringBuffer();
        while(true) {
            cx = _reader.peek();
            if (cx == '\0') throw new Xn("Attempt to read beyond EOF");
            for(int i = 0; i < c.length(); i++) {
                if (cx == c.charAt(i)) return sb.toString();
            }
            _reader.next();
            sb.append(cx);
        }
    }
    String read_tillx(String c) {
        String s = read_till(c);
        _reader.next(); // read the closing char too
        return s;
    }

    public String read_str(String c) {
        return read_tillx(c);
    }

    public Number read_num() {
        String s = read_till(" \n;|");
        // for now.
        return Integer.parseInt(s);
    }
    public int read_sym() {
        String s = read_till(" \n;|");
        d(s);
        return Sym.i(s);
    }
    boolean consume_space() {
        while(true) {
            char ch = _reader.peek();
            switch (ch) {
                case '\0': d("->0"); return false;
                case ' ':
                case '\t':
                    _reader.next();
                    break;
                default: return true;
            }
        }
    }
    public boolean eof() {
        return _reader.eof();
    }
}

